package org.groovymud.object.alive;

import java.io.IOException;
import java.util.Set;

import org.apache.log4j.Logger;
import org.groovymud.engine.event.EventScope;
import org.groovymud.engine.event.IScopedEvent;
import org.groovymud.engine.event.messages.MessageEvent;
import org.groovymud.engine.event.system.MovementEvent;
import org.groovymud.object.AbstractContainer;
import org.groovymud.object.Container;
import org.groovymud.object.MudObject;
import org.groovymud.object.combat.AbstractAttack;
import org.groovymud.object.combat.CombatBehaviour;
import org.groovymud.object.combat.Defence;
import org.groovymud.object.guild.AbstractGuildBehaviour;
import org.groovymud.object.race.AbstractBodyPart;
import org.groovymud.object.race.AbstractRace;
import org.groovymud.object.race.Statistics;
import org.groovymud.object.registry.InventoryHandler;
import org.groovymud.object.room.AbstractRoom;
import org.groovymud.shell.telnetd.ExtendedTerminalIO;
import org.groovymud.utils.Dice;
import org.groovymud.utils.MessageMunger;

/* Copyright 2008 Matthew Corby-Eaglen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
public abstract class AbstractMOB extends AbstractContainer implements Container, Alive {

    private static final String CARRYING_DESC = "carrying";
    private static final String HOLDING_DESC = "holding";
    private static final String WEARING_DESC = "wearing";

    private final transient static Logger logger = Logger.getLogger(AbstractRoom.class);

    private static final double CANNOT_PICK_UP = -1;

    private transient ExtendedTerminalIO terminalIO = null;

    private AbstractRace raceBehaviour;

    private AbstractGuildBehaviour guildBehaviour;

    private boolean dead;

    private String gender;

    private boolean inCombat = false;

    public void initialise() {
    }

    public void heartBeat() {
        if (isInCombat()) {
            // fight!!
            CombatBehaviour combatBehaviour = getGuildBehaviour().getCombatBehaviour();
            combatBehaviour.fight();
        }
        doHeartBeat();
    }

    public ExtendedTerminalIO getTerminalOutput() {
        return terminalIO;
    }

    public void setTerminalOutput(ExtendedTerminalIO output) {
        this.terminalIO = output;
    }

    public void doEvent(IScopedEvent arg) {
        if (arg instanceof MessageEvent) {
            MessageEvent event = (MessageEvent) arg;
            if (getTerminalOutput() != null) {
                try {
                    getTerminalOutput().writeln(event.getMessage(this));
                } catch (IOException e) {
                    logger.error(e, e);
                }
            }
        }
        if (arg instanceof MovementEvent) {
            MovementEvent event = (MovementEvent) arg;

        }
    }

    public String getMessage(String messageKey, String[] replacements) {
        String bucketMessage = null;
        bucketMessage = (String) getRaceBehaviour().getMessageBucket().getMessage(messageKey);
        return MessageMunger.mungeMessage(bucketMessage, replacements);
    }

    public Statistics getStats() {
        return getRaceBehaviour().getStatistics();
    }

    public boolean isDead() {
        return dead;
    }

    public void setDead(boolean dead) {
        this.dead = dead;
    }

    public AbstractGuildBehaviour getGuildBehaviour() {
        return guildBehaviour;
    }

    public void setGuildBehaviour(AbstractGuildBehaviour guildBehaviour) {
        this.guildBehaviour = guildBehaviour;
    }

    public String getGuildName() {
        return guildBehaviour.getGuildName();
    }

    public void setCombatBehaviour(CombatBehaviour combatBehaviour) {
        guildBehaviour.setCombatBehaviour(combatBehaviour);
    }

    public void setGuildName(String guildName) {
        guildBehaviour.setGuildName(guildName);
    }

    public AbstractRace getRaceBehaviour() {
        return raceBehaviour;
    }

    public void setRaceBehaviour(AbstractRace raceBehaviour) {
        this.raceBehaviour = raceBehaviour;
    }

    protected void writeLookHeader(ExtendedTerminalIO stream, Alive looker) throws IOException {
        if (looker.equals(this)) {
            stream.writeln("Looking at yourself?? Vanity it's self!");
            stream.writeln("You look like:");
        } else {
            stream.writeln("You see " + getName() + " the " + getGuildBehaviour().getGuildTitle());
        }
        stream.writeln(getDescription());
    }

    @Override
    public void writeLookBody(ExtendedTerminalIO stream, Alive looker) throws IOException {
        writeLookInventory(stream, WEARING_DESC, getRaceBehaviour().getWearingInventory(), looker);
        writeLookInventory(stream, HOLDING_DESC, getRaceBehaviour().getHoldingInventory(), looker);
        writeLookInventory(stream, CARRYING_DESC, getInventoryHandler(), looker);
    }

    protected void writeLookInventory(ExtendedTerminalIO stream, String invTypeAction, InventoryHandler handler, Alive looker) throws IOException {
        if (looker.equals(this)) {
            stream.writeln("You are " + invTypeAction + ": ");
        } else {
            stream.writeln(getName() + " is " + invTypeAction + ": ");
        }
        String wearing = getContentsDescription(handler.getMudObjectsMap(), looker, false);
        stream.writeln(wearing.equals("") ? "Nothing." : wearing);
    }

    protected void writeLookFooter(ExtendedTerminalIO stream) throws IOException {
    }

    @Override
    public boolean doCommand(String command, String argsAsString, MudObject performingObject) {
        boolean done = false;

        if (performingObject.equals(this)) {
            done = doCommandInContents(command, argsAsString, (Container) performingObject, getItems());
            if (!done) {
                done = doCommandInContents(command, argsAsString, (Container) performingObject, getItemsFromInventoryHandler(getRaceBehaviour().getHoldingInventory()));
            }
            if (!done) {
                done = doCommandInContents(command, argsAsString, (Container) performingObject, getItemsFromInventoryHandler(getRaceBehaviour().getWearingInventory()));
            }
        }
        if (!done) {
            done = super.doCommand(command, argsAsString, performingObject);
        }
        return done;
    }

    public void doLook(Alive looker) {
        super.doLook(looker);

    }

    public double getWeight() {
        return CANNOT_PICK_UP;
    }

    public EventScope getScope() {
        return EventScope.PLAYER_SCOPE;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getGender() {
        return gender;
    }

    @Override
    public long defend(AbstractAttack attack, long damage, long attackToHit, long roll) {
        long defend = doDefend(attack, damage, attackToHit, roll);
        Dice dice = new Dice(1, 100);
        AbstractBodyPart part = getRaceBehaviour().getHitBodyPart(dice.roll());
        for (Defence defence : part.getDefences()) {
            defend = defence.defend(attack, damage, attackToHit, roll);
        }
        return defend;
    }

    /**
     * extra defences this mob may have
     * 
     * @param nextAttack
     * @param damage
     * @param attackToHit
     * @param roll
     * @return
     */
    public abstract long doDefend(AbstractAttack nextAttack, long damage, long attackToHit, long roll);

    public Set<AbstractAttack> getAttacks() {
        // TODO Auto-generated method stub
        return getRaceBehaviour().getAttacks();
    }

    @Override
    public long getMaxCapacity() {
        return getStats().getStrength();
    }

    public void setInCombat(boolean inCombat) {
        this.inCombat = inCombat;
    }

    public boolean isInCombat() {
        return inCombat;
    }

}
